﻿using System;
using System.Runtime.InteropServices;

namespace ExRev.Engine
{
    [Serializable, StructLayout(LayoutKind.Sequential)]
    public struct Matrix
    {
        public float M11;
        public float M12;
        public float M13;
        public float M14;
        public float M21;
        public float M22;
        public float M23;
        public float M24;
        public float M31;
        public float M32;
        public float M33;
        public float M34;
        public float M41;
        public float M42;
        public float M43;
        public float M44;

        public static Matrix Identity
        {
            get { return identity; }
        }
        private static Matrix identity;

        public Matrix(float m11, float m12, float m13, float m14,
                      float m21, float m22, float m23, float m24,
                      float m31, float m32, float m33, float m34,
                      float m41, float m42, float m43, float m44)
        {
            M11 = m11; M12 = m12; M13 = m13; M14 = m14;
            M21 = m21; M22 = m22; M23 = m23; M24 = m24;
            M31 = m31; M32 = m32; M33 = m33; M34 = m34;
            M41 = m41; M42 = m42; M43 = m43; M44 = m44;
        }

        static Matrix()
        {
            identity = new Matrix(1.0f, 0.0f, 0.0f, 0.0f,
                                  0.0f, 1.0f, 0.0f, 0.0f,
                                  0.0f, 0.0f, 1.0f, 0.0f,
                                  0.0f, 0.0f, 0.0f, 1.0f);
        }

        public override string ToString()
        {
            return "{"
                 + string.Format("{{M11:{0} M12:{1} M13:{2} M14:{3}}} ", new object[] { M11, M12, M13, M14 })
                 + string.Format("{{M21:{0} M22:{1} M23:{2} M24:{3}}} ", new object[] { M21, M22, M23, M24 })
                 + string.Format("{{M31:{0} M32:{1} M33:{2} M34:{3}}} ", new object[] { M31, M32, M33, M34 })
                 + string.Format("{{M41:{0} M42:{1} M43:{2} M44:{3}}} ", new object[] { M41, M42, M43, M44 })
                 + "}";
        }

        public bool Equals(Matrix other)
        {
            return this.M11 == other.M11 && this.M12 == other.M12 && this.M13 == other.M13 && this.M14 == other.M14
                && this.M21 == other.M21 && this.M22 == other.M22 && this.M23 == other.M23 && this.M24 == other.M24
                && this.M31 == other.M31 && this.M32 == other.M32 && this.M33 == other.M33 && this.M34 == other.M34
                && this.M41 == other.M41 && this.M42 == other.M42 && this.M43 == other.M43 && this.M44 == other.M44;
        }

        public override bool Equals(object obj)
        {
            return (obj is Matrix) ? Equals((Matrix)obj) : false;
        }

        public override int GetHashCode()
        {
            return M11.GetHashCode() + M12.GetHashCode() + M13.GetHashCode() + M14.GetHashCode()
                 + M21.GetHashCode() + M22.GetHashCode() + M23.GetHashCode() + M24.GetHashCode()
                 + M31.GetHashCode() + M32.GetHashCode() + M33.GetHashCode() + M34.GetHashCode()
                 + M41.GetHashCode() + M42.GetHashCode() + M43.GetHashCode() + M44.GetHashCode();
        }

        public static Matrix CreateTranslation(Vector3 position)
        {
            return new Matrix(1.0f, 0.0f, 0.0f, 0.0f,
                              0.0f, 1.0f, 0.0f, 0.0f,
                              0.0f, 0.0f, 1.0f, 0.0f,
                              position.X, position.Y, position.Z, 1.0f);
        }

        public static Matrix CreateTranslation(float xPosition, float yPosition, float zPosition)
        {
            return new Matrix(1.0f, 0.0f, 0.0f, 0.0f,
                              0.0f, 1.0f, 0.0f, 0.0f,
                              0.0f, 0.0f, 1.0f, 0.0f,
                              xPosition, yPosition, zPosition, 1.0f);
        }

        public static Matrix CreateScale(Vector3 scales)
        {
            return new Matrix(scales.X, 0.0f, 0.0f, 0.0f,
                              0.0f, scales.Y, 0.0f, 0.0f,
                              0.0f, 0.0f, scales.Z, 0.0f,
                              0.0f, 0.0f, 0.0f, 1.0f);
        }

        public static Matrix CreateScale(float xScale, float yScale, float zScale)
        {
            return new Matrix(xScale, 0.0f, 0.0f, 0.0f,
                              0.0f, yScale, 0.0f, 0.0f,
                              0.0f, 0.0f, zScale, 0.0f,
                              0.0f, 0.0f, 0.0f, 1.0f);
        }

        public static Matrix CreateScale(float scale)
        {
            return new Matrix(scale, 0.0f, 0.0f, 0.0f,
                              0.0f, scale, 0.0f, 0.0f,
                              0.0f, 0.0f, scale, 0.0f,
                              0.0f, 0.0f, 0.0f, 1.0f);
        }

        public static Matrix CreateRotationX(float radians)
        {
            float cos = (float)Math.Cos(radians);
            float sin = (float)Math.Sin(radians);
            return new Matrix(1.0f, 0.0f, 0.0f, 0.0f,
                              0.0f, cos, sin, 0.0f,
                              0.0f, -sin, cos, 0.0f,
                              0.0f, 0.0f, 0.0f, 1.0f);
        }

        public static Matrix CreateRotationY(float radians)
        {
            float cos = (float)Math.Cos(radians);
            float sin = (float)Math.Sin(radians);
            return new Matrix(cos, 0.0f, -sin, 0.0f,
                              0.0f, 1.0f, 0.0f, 0.0f,
                              sin, 0.0f, cos, 0.0f,
                              0.0f, 0.0f, 0.0f, 1.0f);
        }

        public static Matrix CreateRotationZ(float radians)
        {
            float cos = (float)Math.Cos(radians);
            float sin = (float)Math.Sin(radians);
            return new Matrix(cos, sin, 0.0f, 0.0f,
                              -sin, cos, 0.0f, 0.0f,
                              0.0f, 0.0f, 1.0f, 0.0f,
                              0.0f, 0.0f, 0.0f, 1.0f);
        }

        public static Matrix CreateFromAxisAngle(Vector3 axis, float angle)
        {
            float x = axis.X;
            float y = axis.Y;
            float z = axis.Z;
            float sin = (float)Math.Sin((double)angle);
            float cos = (float)Math.Cos((double)angle);
            float xx = x * x;
            float yy = y * y;
            float zz = z * z;
            float xy = x * y;
            float xz = x * z;
            float yz = y * z;
            return new Matrix(xx + (cos * (1f - xx)), (xy - (cos * xy)) + (sin * z), (xz - (cos * xz)) - (sin * y), 0.0f,
                              (xy - (cos * xy)) - (sin * z), yy + (cos * (1f - yy)), (yz - (cos * yz)) + (sin * x), 0.0f,
                              (xz - (cos * xz)) + (sin * y), (yz - (cos * yz)) - (sin * x), zz + (cos * (1f - zz)), 0.0f,
                              0.0f, 0.0f, 0.0f, 1.0f);
        }

        public static Matrix CreatePerspectiveFieldOfView(float fieldOfView, float aspectRatio, float nearPlaneDistance, float farPlaneDistance)
        {
            if (fieldOfView <= 0.0f || fieldOfView >= (float)Math.PI) {
                throw new ArgumentOutOfRangeException("fieldOfView", "fieldOfView takes a value between 0 and Pi (180 degrees) in radians.");
            }
            if (nearPlaneDistance <= 0.0f) {
                throw new ArgumentOutOfRangeException("nearPlaneDistance", "You should specify positive value for nearPlaneDistance.");
            }
            if (farPlaneDistance <= 0.0f) {
                throw new ArgumentOutOfRangeException("farPlaneDistance", "You should specify positive value for farPlaneDistance.");
            }
            if (nearPlaneDistance >= farPlaneDistance) {
                throw new ArgumentOutOfRangeException("nearPlaneDistance",
                    "Near plane distance is larger than far plane distance. Near plane distance must be smaller than Far plane distance.");
            }
            float n = 1.0f / ((float)Math.Tan(fieldOfView * 0.5f));
            float ns = n / aspectRatio;
            return new Matrix(ns, 0.0f, 0.0f, 0.0f,
                              0.0f, n, 0.0f, 0.0f,
                              0.0f, 0.0f, farPlaneDistance / (nearPlaneDistance - farPlaneDistance), -1.0f,
                              0.0f, 0.0f, (nearPlaneDistance * farPlaneDistance) / (nearPlaneDistance - farPlaneDistance), 0.0f);
        }

        public static Matrix CreatePerspective(float width, float height, float nearPlaneDistance, float farPlaneDistance)
        {
            if (nearPlaneDistance <= 0.0f) {
                throw new ArgumentOutOfRangeException("nearPlaneDistance", "You should specify positive value for nearPlaneDistance.");
            }
            if (farPlaneDistance <= 0.0f) {
                throw new ArgumentOutOfRangeException("farPlaneDistance", "You should specify positive value for farPlaneDistance.");
            }
            if (nearPlaneDistance >= farPlaneDistance) {
                throw new ArgumentOutOfRangeException("nearPlaneDistance",
                    "Near plane distance is larger than far plane distance. Near plane distance must be smaller than Far plane distance.");
            }
            return new Matrix((2.0f * nearPlaneDistance) / width, 0.0f, 0.0f, 0.0f,
                              0.0f, (2.0f * nearPlaneDistance) / height, 0.0f, 0.0f,
                              0.0f, 0.0f, farPlaneDistance / (nearPlaneDistance - farPlaneDistance), -1.0f,
                              0.0f, 0.0f, (nearPlaneDistance * farPlaneDistance) / (nearPlaneDistance - farPlaneDistance), 0.0f);

        }

        public static Matrix CreatePerspectiveOffCenter(float left, float right, float bottom, float top, float nearPlaneDistance, float farPlaneDistance)
        {
            if (nearPlaneDistance <= 0.0f) {
                throw new ArgumentOutOfRangeException("nearPlaneDistance", "You should specify positive value for nearPlaneDistance.");
            }
            if (farPlaneDistance <= 0.0f) {
                throw new ArgumentOutOfRangeException("farPlaneDistance", "You should specify positive value for farPlaneDistance.");
            }
            if (nearPlaneDistance >= farPlaneDistance) {
                throw new ArgumentOutOfRangeException("nearPlaneDistance",
                    "Near plane distance is larger than far plane distance. Near plane distance must be smaller than Far plane distance.");
            }
            return new Matrix((2.0f * nearPlaneDistance) / (right - left), 0.0f, 0.0f, 0.0f,
                              0.0f, (2.0f * nearPlaneDistance) / (top - bottom), 0.0f, 0.0f,
                              (left + right) / (right - left), (top + bottom) / (top - bottom), farPlaneDistance / (nearPlaneDistance - farPlaneDistance), -1.0f,
                              0.0f, 0.0f, (nearPlaneDistance * farPlaneDistance) / (nearPlaneDistance - farPlaneDistance), 0.0f);

        }

        public static Matrix CreateOrthographic(float width, float height, float zNearPlane, float zFarPlane)
        {
            return new Matrix(2.0f / width, 0.0f, 0.0f, 0.0f,
                              0.0f, 2.0f / height, 0.0f, 0.0f,
                              0.0f, 0.0f, 1.0f / (zNearPlane - zFarPlane), 0.0f,
                              0.0f, 0.0f, zNearPlane / (zNearPlane - zFarPlane), 1.0f);

        }

        public static Matrix CreateOrthographicOffCenter(float left, float right, float bottom, float top, float zNearPlane, float zFarPlane)
        {
            return new Matrix(2.0f / (right - left), 0.0f, 0.0f, 0.0f,
                              0.0f, 2.0f / (top - bottom), 0.0f, 0.0f,
                              0.0f, 0.0f, 1.0f / (zNearPlane - zFarPlane), 0.0f,
                              (left + right) / (left - right), (top + bottom) / (bottom - top), zNearPlane / (zNearPlane - zFarPlane), 1.0f);
        }

        public static Matrix CreateLookAt(Vector3 cameraPosition, Vector3 cameraTarget, Vector3 cameraUpVector)
        {
            Vector3 zDirection = Vector3.Normalize(cameraPosition - cameraTarget);
            Vector3 xDirection = Vector3.Cross(cameraUpVector, zDirection);
            Vector3 yDirection = Vector3.Cross(zDirection, xDirection);
            return new Matrix(xDirection.X, yDirection.X, zDirection.X, 0.0f,
                              xDirection.Y, yDirection.Y, zDirection.Y, 0.0f,
                              xDirection.Z, yDirection.Z, zDirection.Z, 0.0f,
                              -Vector3.Dot(xDirection, cameraPosition), -Vector3.Dot(yDirection, cameraPosition), -Vector3.Dot(zDirection, cameraPosition), 1.0f);
        }

        public static Matrix CreateWorld(Vector3 position, Vector3 forward, Vector3 up)
        {
            Vector3 zDirection = Vector3.Normalize(-forward);
            Vector3 xDirection = Vector3.Normalize(Vector3.Cross(up, zDirection));
            Vector3 yDirection = Vector3.Cross(zDirection, xDirection);
            return new Matrix(xDirection.X, xDirection.Y, xDirection.Z, 0.0f,
                              yDirection.X, yDirection.Y, yDirection.Z, 0.0f,
                              zDirection.X, zDirection.Y, zDirection.Z, 0.0f,
                              position.X, position.Y, position.Z, 1.0f);
        }

        public static Matrix CreateFromQuaternion(Quaternion quaternion)
        {
            float x = quaternion.X * quaternion.X;
            float y = quaternion.Y * quaternion.Y;
            float z = quaternion.Z * quaternion.Z;
            float xy = quaternion.X * quaternion.Y;
            float zw = quaternion.Z * quaternion.W;
            float zx = quaternion.Z * quaternion.X;
            float yw = quaternion.Y * quaternion.W;
            float yz = quaternion.Y * quaternion.Z;
            float xw = quaternion.X * quaternion.W;
            return new Matrix(1.0f - (2.0f * (y + z)), 2.0f * (xy + zw), 2.0f * (zx - yw), 0.0f,
                              2.0f * (xy - zw), 1.0f - (2.0f * (z + x)), 2.0f * (yz + xw), 0.0f,
                              2.0f * (zx + yw), 2.0f * (yz - xw), 1.0f - (2.0f * (y + x)), 0.0f,
                              0.0f, 0.0f, 0.0f, 1.0f);
        }

        public static Matrix Transform(Matrix value, Quaternion rotation)
        {
            float x = rotation.X + rotation.X;
            float y = rotation.Y + rotation.Y;
            float z = rotation.Z + rotation.Z;
            float wx = rotation.W * x;
            float wy = rotation.W * y;
            float wz = rotation.W * z;
            float xx = rotation.X * x;
            float xy = rotation.X * y;
            float zy = rotation.X * z;
            float yy = rotation.Y * y;
            float yz = rotation.Y * z;
            float zz = rotation.Z * z;
            float n11 = (1f - yy) - zz;
            float n12 = xy - wz;
            float n13 = zy + wy;
            float n21 = xy + wz;
            float n22 = (1f - xx) - zz;
            float n23 = yz - wx;
            float n31 = zy - wy;
            float n32 = yz + wx;
            float n33 = (1f - xx) - yy;
            return new Matrix(((value.M11 * n11) + (value.M12 * n12)) + (value.M13 * n13), ((value.M11 * n21) + (value.M12 * n22)) + (value.M13 * n23), ((value.M11 * n31) + (value.M12 * n32)) + (value.M13 * n33), value.M14,
                              ((value.M21 * n11) + (value.M22 * n12)) + (value.M23 * n13), ((value.M21 * n21) + (value.M22 * n22)) + (value.M23 * n23), ((value.M21 * n31) + (value.M22 * n32)) + (value.M23 * n33), value.M24,
                              ((value.M31 * n11) + (value.M32 * n12)) + (value.M33 * n13), ((value.M31 * n21) + (value.M32 * n22)) + (value.M33 * n23), ((value.M31 * n31) + (value.M32 * n32)) + (value.M33 * n33), value.M34,
                              ((value.M41 * n11) + (value.M42 * n12)) + (value.M43 * n13), ((value.M41 * n21) + (value.M42 * n22)) + (value.M43 * n23), ((value.M41 * n31) + (value.M42 * n32)) + (value.M43 * n33), value.M44);
        }

        public static Matrix Transpose(Matrix matrix)
        {
            return new Matrix(matrix.M11, matrix.M21, matrix.M31, matrix.M41,
                              matrix.M12, matrix.M22, matrix.M12, matrix.M42,
                              matrix.M13, matrix.M23, matrix.M33, matrix.M43,
                              matrix.M14, matrix.M24, matrix.M34, matrix.M44);
        }

        public float Determinant()
        {
            float d1 = (M33 * M44) - (M34 * M43);
            float d2 = (M32 * M44) - (M34 * M42);
            float d3 = (M32 * M43) - (M33 * M42);
            float d4 = (M31 * M44) - (M34 * M41);
            float d5 = (M31 * M43) - (M33 * M41);
            float d6 = (M31 * M42) - (M32 * M41);
            return  (((M11 * (((M22 * d1) - (M23 * d2)) + (M24 * d3)))
                    - (M12 * (((M21 * d1) - (M23 * d4)) + (M24 * d5))))
                    + (M13 * (((M21 * d2) - (M22 * d4)) + (M24 * d6))))
                    - (M14 * (((M21 * d3) - (M22 * d5)) + (M23 * d6)));
        }

        public static Matrix Invert(Matrix matrix)
        {
            Matrix result;
            float m11 = matrix.M11;
            float m12 = matrix.M12;
            float m13 = matrix.M13;
            float m14 = matrix.M14;
            float m21 = matrix.M21;
            float m22 = matrix.M22;
            float m23 = matrix.M23;
            float m24 = matrix.M24;
            float m31 = matrix.M31;
            float m32 = matrix.M32;
            float m33 = matrix.M33;
            float m34 = matrix.M34;
            float m41 = matrix.M41;
            float m42 = matrix.M42;
            float m43 = matrix.M43;
            float m44 = matrix.M44;
            float d1 = (m33 * m44) - (m34 * m43);
            float d2 = (m32 * m44) - (m34 * m42);
            float d3 = (m32 * m43) - (m33 * m42);
            float d4 = (m31 * m44) - (m34 * m41);
            float d5 = (m31 * m43) - (m33 * m41);
            float d6 = (m31 * m42) - (m32 * m41);
            float n1 =   ((m22 * d1) - (m23 * d2)) + (m24 * d3);
            float n2 = -(((m21 * d1) - (m23 * d4)) + (m24 * d5));
            float n3 =   ((m21 * d2) - (m22 * d4)) + (m24 * d6);
            float n4 = -(((m21 * d3) - (m22 * d5)) + (m23 * d6));
            float d = 1f / ((((m11 * n1) + (m12 * n2)) + (m13 * n3)) + (m14 * n4));
            result.M11 = n1 * d;
            result.M21 = n2 * d;
            result.M31 = n3 * d;
            result.M41 = n4 * d;
            result.M12 = -(((m12 * d1) - (m13 * d2)) + (m14 * d3)) * d;
            result.M22 =  (((m11 * d1) - (m13 * d4)) + (m14 * d5)) * d;
            result.M32 = -(((m11 * d2) - (m12 * d4)) + (m14 * d6)) * d;
            result.M42 =  (((m11 * d3) - (m12 * d5)) + (m13 * d6)) * d;
            d1 = (m23 * m44) - (m24 * m43);
            d2 = (m22 * m44) - (m24 * m42);
            d3 = (m22 * m43) - (m23 * m42);
            d4 = (m21 * m44) - (m24 * m41);
            d5 = (m21 * m43) - (m23 * m41);
            d6 = (m21 * m42) - (m22 * m41);
            result.M13 =  (((m12 * d1) - (m13 * d2)) + (m14 * d3)) * d;
            result.M23 = -(((m11 * d1) - (m13 * d4)) + (m14 * d5)) * d;
            result.M33 =  (((m11 * d2) - (m12 * d4)) + (m14 * d6)) * d;
            result.M43 = -(((m11 * d3) - (m12 * d5)) + (m13 * d6)) * d;
            d1 = (m23 * m34) - (m24 * m33);
            d2 = (m22 * m34) - (m24 * m32);
            d3 = (m22 * m33) - (m23 * m32);
            d4 = (m21 * m34) - (m24 * m31);
            d5 = (m21 * m33) - (m23 * m31);
            d6 = (m21 * m32) - (m22 * m31);
            result.M14 = -(((m12 * d1) - (m13 * d2)) + (m14 * d3)) * d;
            result.M24 =  (((m11 * d1) - (m13 * d4)) + (m14 * d5)) * d;
            result.M34 = -(((m11 * d2) - (m12 * d4)) + (m14 * d6)) * d;
            result.M44 =  (((m11 * d3) - (m12 * d5)) + (m13 * d6)) * d;
            return result;
        }

        public static Matrix Lerp(Matrix matrix1, Matrix matrix2, float amount)
        {
            Matrix result;
            result.M11 = matrix1.M11 + ((matrix2.M11 - matrix1.M11) * amount);
            result.M12 = matrix1.M12 + ((matrix2.M12 - matrix1.M12) * amount);
            result.M13 = matrix1.M13 + ((matrix2.M13 - matrix1.M13) * amount);
            result.M14 = matrix1.M14 + ((matrix2.M14 - matrix1.M14) * amount);
            result.M21 = matrix1.M21 + ((matrix2.M21 - matrix1.M21) * amount);
            result.M22 = matrix1.M22 + ((matrix2.M22 - matrix1.M22) * amount);
            result.M23 = matrix1.M23 + ((matrix2.M23 - matrix1.M23) * amount);
            result.M24 = matrix1.M24 + ((matrix2.M24 - matrix1.M24) * amount);
            result.M31 = matrix1.M31 + ((matrix2.M31 - matrix1.M31) * amount);
            result.M32 = matrix1.M32 + ((matrix2.M32 - matrix1.M32) * amount);
            result.M33 = matrix1.M33 + ((matrix2.M33 - matrix1.M33) * amount);
            result.M34 = matrix1.M34 + ((matrix2.M34 - matrix1.M34) * amount);
            result.M41 = matrix1.M41 + ((matrix2.M41 - matrix1.M41) * amount);
            result.M42 = matrix1.M42 + ((matrix2.M42 - matrix1.M42) * amount);
            result.M43 = matrix1.M43 + ((matrix2.M43 - matrix1.M43) * amount);
            result.M44 = matrix1.M44 + ((matrix2.M44 - matrix1.M44) * amount);
            return result;

        }

        public static bool operator ==(Matrix matrix1, Matrix matrix2)
        {
            return matrix1.M11 == matrix2.M11 && matrix1.M12 == matrix2.M12 && matrix1.M13 == matrix2.M13 && matrix1.M14 == matrix2.M14
                && matrix1.M21 == matrix2.M21 && matrix1.M22 == matrix2.M22 && matrix1.M23 == matrix2.M23 && matrix1.M24 == matrix2.M24
                && matrix1.M31 == matrix2.M31 && matrix1.M32 == matrix2.M32 && matrix1.M33 == matrix2.M33 && matrix1.M34 == matrix2.M34
                && matrix1.M41 == matrix2.M41 && matrix1.M42 == matrix2.M42 && matrix1.M43 == matrix2.M43 && matrix1.M44 == matrix2.M44;
        }

        public static bool operator !=(Matrix matrix1, Matrix matrix2)
        {
            return matrix1.M11 != matrix2.M11 || matrix1.M12 != matrix2.M12 || matrix1.M13 != matrix2.M13 || matrix1.M14 != matrix2.M14
                || matrix1.M21 != matrix2.M21 || matrix1.M22 != matrix2.M22 || matrix1.M23 != matrix2.M23 || matrix1.M24 != matrix2.M24
                || matrix1.M31 != matrix2.M31 || matrix1.M32 != matrix2.M32 || matrix1.M33 != matrix2.M33 || matrix1.M34 != matrix2.M34
                || matrix1.M41 != matrix2.M41 || matrix1.M42 != matrix2.M42 || matrix1.M43 != matrix2.M43 || matrix1.M44 != matrix2.M44;
        }

        public static Matrix operator+(Matrix matrix1, Matrix matrix2)
        {
            return new Matrix(matrix1.M11 + matrix2.M11, matrix1.M12 + matrix2.M12, matrix1.M13 + matrix2.M13, matrix1.M14 + matrix2.M14,
                              matrix1.M21 + matrix2.M21, matrix1.M22 + matrix2.M22, matrix1.M23 + matrix2.M23, matrix1.M24 + matrix2.M24,
                              matrix1.M31 + matrix2.M31, matrix1.M32 + matrix2.M32, matrix1.M33 + matrix2.M33, matrix1.M34 + matrix2.M34,
                              matrix1.M41 + matrix2.M41, matrix1.M42 + matrix2.M42, matrix1.M43 + matrix2.M43, matrix1.M44 + matrix2.M44);
        }

        public static Matrix operator-(Matrix matrix1, Matrix matrix2)
        {
            return new Matrix(matrix1.M11 - matrix2.M11, matrix1.M12 - matrix2.M12, matrix1.M13 - matrix2.M13, matrix1.M14 - matrix2.M14,
                              matrix1.M21 - matrix2.M21, matrix1.M22 - matrix2.M22, matrix1.M23 - matrix2.M23, matrix1.M24 - matrix2.M24,
                              matrix1.M31 - matrix2.M31, matrix1.M32 - matrix2.M32, matrix1.M33 - matrix2.M33, matrix1.M34 - matrix2.M34,
                              matrix1.M41 - matrix2.M41, matrix1.M42 - matrix2.M42, matrix1.M43 - matrix2.M43, matrix1.M44 - matrix2.M44);
        }

        public static Matrix operator-(Matrix matrix)
        {
            return new Matrix(-matrix.M11, -matrix.M12, -matrix.M13, -matrix.M14,
                              -matrix.M21, -matrix.M22, -matrix.M23, -matrix.M24,
                              -matrix.M31, -matrix.M32, -matrix.M33, -matrix.M34,
                              -matrix.M41, -matrix.M42, -matrix.M43, -matrix.M44);
        }

        public static Matrix operator*(Matrix matrix1, Matrix matrix2)
        {
            Matrix result;
            result.M11 = (((matrix1.M11 * matrix2.M11) + (matrix1.M12 * matrix2.M21)) + (matrix1.M13 * matrix2.M31)) + (matrix1.M14 * matrix2.M41);
            result.M12 = (((matrix1.M11 * matrix2.M12) + (matrix1.M12 * matrix2.M22)) + (matrix1.M13 * matrix2.M32)) + (matrix1.M14 * matrix2.M42);
            result.M13 = (((matrix1.M11 * matrix2.M13) + (matrix1.M12 * matrix2.M23)) + (matrix1.M13 * matrix2.M33)) + (matrix1.M14 * matrix2.M43);
            result.M14 = (((matrix1.M11 * matrix2.M14) + (matrix1.M12 * matrix2.M24)) + (matrix1.M13 * matrix2.M34)) + (matrix1.M14 * matrix2.M44);
            result.M21 = (((matrix1.M21 * matrix2.M11) + (matrix1.M22 * matrix2.M21)) + (matrix1.M23 * matrix2.M31)) + (matrix1.M24 * matrix2.M41);
            result.M22 = (((matrix1.M21 * matrix2.M12) + (matrix1.M22 * matrix2.M22)) + (matrix1.M23 * matrix2.M32)) + (matrix1.M24 * matrix2.M42);
            result.M23 = (((matrix1.M21 * matrix2.M13) + (matrix1.M22 * matrix2.M23)) + (matrix1.M23 * matrix2.M33)) + (matrix1.M24 * matrix2.M43);
            result.M24 = (((matrix1.M21 * matrix2.M14) + (matrix1.M22 * matrix2.M24)) + (matrix1.M23 * matrix2.M34)) + (matrix1.M24 * matrix2.M44);
            result.M31 = (((matrix1.M31 * matrix2.M11) + (matrix1.M32 * matrix2.M21)) + (matrix1.M33 * matrix2.M31)) + (matrix1.M34 * matrix2.M41);
            result.M32 = (((matrix1.M31 * matrix2.M12) + (matrix1.M32 * matrix2.M22)) + (matrix1.M33 * matrix2.M32)) + (matrix1.M34 * matrix2.M42);
            result.M33 = (((matrix1.M31 * matrix2.M13) + (matrix1.M32 * matrix2.M23)) + (matrix1.M33 * matrix2.M33)) + (matrix1.M34 * matrix2.M43);
            result.M34 = (((matrix1.M31 * matrix2.M14) + (matrix1.M32 * matrix2.M24)) + (matrix1.M33 * matrix2.M34)) + (matrix1.M34 * matrix2.M44);
            result.M41 = (((matrix1.M41 * matrix2.M11) + (matrix1.M42 * matrix2.M21)) + (matrix1.M43 * matrix2.M31)) + (matrix1.M44 * matrix2.M41);
            result.M42 = (((matrix1.M41 * matrix2.M12) + (matrix1.M42 * matrix2.M22)) + (matrix1.M43 * matrix2.M32)) + (matrix1.M44 * matrix2.M42);
            result.M43 = (((matrix1.M41 * matrix2.M13) + (matrix1.M42 * matrix2.M23)) + (matrix1.M43 * matrix2.M33)) + (matrix1.M44 * matrix2.M43);
            result.M44 = (((matrix1.M41 * matrix2.M14) + (matrix1.M42 * matrix2.M24)) + (matrix1.M43 * matrix2.M34)) + (matrix1.M44 * matrix2.M44);
            return result;
        }

        public static Matrix operator*(Matrix matrix, float scaleFactor)
        {
            return new Matrix(matrix.M11 * scaleFactor, matrix.M12 * scaleFactor, matrix.M13 * scaleFactor, matrix.M14 * scaleFactor,
                              matrix.M21 * scaleFactor, matrix.M22 * scaleFactor, matrix.M23 * scaleFactor, matrix.M24 * scaleFactor,
                              matrix.M31 * scaleFactor, matrix.M32 * scaleFactor, matrix.M33 * scaleFactor, matrix.M34 * scaleFactor,
                              matrix.M41 * scaleFactor, matrix.M42 * scaleFactor, matrix.M43 * scaleFactor, matrix.M44 * scaleFactor);
        }

        public static Matrix operator*(float scaleFactor, Matrix matrix)
        {
            return new Matrix(matrix.M11 * scaleFactor, matrix.M12 * scaleFactor, matrix.M13 * scaleFactor, matrix.M14 * scaleFactor,
                              matrix.M21 * scaleFactor, matrix.M22 * scaleFactor, matrix.M23 * scaleFactor, matrix.M24 * scaleFactor,
                              matrix.M31 * scaleFactor, matrix.M32 * scaleFactor, matrix.M33 * scaleFactor, matrix.M34 * scaleFactor,
                              matrix.M41 * scaleFactor, matrix.M42 * scaleFactor, matrix.M43 * scaleFactor, matrix.M44 * scaleFactor);
        }

        public static Matrix operator/(Matrix matrix1, Matrix matrix2)
        {
            return new Matrix(matrix1.M11 / matrix2.M11, matrix1.M12 / matrix2.M12, matrix1.M13 / matrix2.M13, matrix1.M14 / matrix2.M14,
                              matrix1.M21 / matrix2.M21, matrix1.M22 / matrix2.M22, matrix1.M23 / matrix2.M23, matrix1.M24 / matrix2.M24,
                              matrix1.M31 / matrix2.M31, matrix1.M32 / matrix2.M32, matrix1.M33 / matrix2.M33, matrix1.M34 / matrix2.M34,
                              matrix1.M41 / matrix2.M41, matrix1.M42 / matrix2.M42, matrix1.M43 / matrix2.M43, matrix1.M44 / matrix2.M44);
        }

        public static Matrix operator /(Matrix matrix, float divider)
        {
            float scaleFactor = 1.0f / divider;
            return new Matrix(matrix.M11 * scaleFactor, matrix.M12 * scaleFactor, matrix.M13 * scaleFactor, matrix.M14 * scaleFactor,
                              matrix.M21 * scaleFactor, matrix.M22 * scaleFactor, matrix.M23 * scaleFactor, matrix.M24 * scaleFactor,
                              matrix.M31 * scaleFactor, matrix.M32 * scaleFactor, matrix.M33 * scaleFactor, matrix.M34 * scaleFactor,
                              matrix.M41 * scaleFactor, matrix.M42 * scaleFactor, matrix.M43 * scaleFactor, matrix.M44 * scaleFactor);
        }
    }
}
